package featureSelection;

import java.io.BufferedWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;

import opennlp.maxent.GISModel;

import maxEntClassifier.U;

public class FeatureStore {

	public Hashtable<String, Integer> featureCount;
	public Hashtable<String, Integer> predicateCount;
	public Hashtable<String, Integer> labelCount;

	public Hashtable<String, Double> featurePrecision;
	public Hashtable<String, Double> featureRecall;
	public Hashtable<String, Double> featureFMeasure;
	public Hashtable<String, Double> featureMI;
	public Hashtable<String, Double> featureCHI;
	public Hashtable<String, Double> predicateCHI;
	public HashSet<String> p1FeatureSet;

	public int itemCount;
	public int featureNumCount;

	public static double f_beta;
	public int labelNum;

	@SuppressWarnings("unchecked")
	public FeatureStore(Hashtable<String, Integer> featureCountTable,
			Hashtable<String, Integer> _YCount) {

		labelCount = _YCount;
		featureCount = featureCountTable;

		featurePrecision = new Hashtable<String, Double>();
		featureRecall = new Hashtable<String, Double>();
		predicateCount = new Hashtable<String, Integer>();
		featureMI = new Hashtable<String, Double>();
		featureCHI = new Hashtable<String, Double>();
		predicateCHI = new Hashtable<String, Double>();
		p1FeatureSet = new HashSet<String>();
		Iterator it = featureCountTable.entrySet().iterator();
		while (it.hasNext()) {
			Map.Entry entry = (Map.Entry) it.next();
			String feature = entry.getKey().toString();
			int value = (Integer) entry.getValue();

			featurePrecision.put(feature, (double) value);
			featureRecall.put(feature, (double) value);
			featureMI.put(feature, 0.0);
			featureCHI.put(feature, 0.0);
			int colon = feature.lastIndexOf(':');
			String key = feature.substring(0, colon);
			int c = 0;
			if (null != predicateCount.get(key))
				c = (Integer) predicateCount.get(key);
			predicateCount.put(key, c + value);
		}

		// count precision
		for (String key : featurePrecision.keySet()) {
			String x = U.getFP(key);
			featurePrecision.put(key, featurePrecision.get(key)
					/ predicateCount.get(x));
		}

		for (String f : featurePrecision.keySet()) {
			if (1 == featurePrecision.get(f))
				p1FeatureSet.add(f);
		}

		// count recall
		for (String key : featureRecall.keySet()) {
			String l = key.substring(key.lastIndexOf(":") + 1, key.length());
			featureRecall.put(key, featureRecall.get(key) / labelCount.get(l));
		}

		// f measure
		featureFMeasure = new Hashtable<String, Double>();
		for (String key : featureRecall.keySet()) {
			double p = featurePrecision.get(key);
			double r = featureRecall.get(key);
			featureFMeasure.put(key, (1 + f_beta * f_beta) * p * r
					/ (f_beta * f_beta * p + r));
		}

		itemCount = 0;
		for (String l : labelCount.keySet()) {
			itemCount += labelCount.get(l);
		}

		featureNumCount = 0;
		for (String feature : featureCount.keySet())
			featureNumCount += featureCount.get(feature);

		// MI
		for (String key : featureRecall.keySet())
			featureMI.put(key, getMutualInfo(key));

		// CHI
		for (String p : predicateCount.keySet())
			for (String l : labelCount.keySet()) {
				featureCHI.put(p + ":" + l, getFeatureCHI(p + ":" + l));
			}

		// predicateCHI
		for (String predicate : predicateCount.keySet())
			predicateCHI.put(predicate, getPredicateCHI(predicate));
	}

	public int getFeatureN10(String f) {
		return predicateCount.get(U.getFP(f)) - featureCount.get(f);
	}

	public int getFeatureN01(String f) {
		return labelCount.get(U.getFL(f)) - featureCount.get(f);
	}

	public int getFeatureN00(String f) {
		return itemCount - labelCount.get(U.getFL(f))
				- predicateCount.get(U.getFP(f)) + featureCount.get(f);
	}

	public double precision(String f) {
		return featurePrecision.get(f);
	}

	public double recall(String f) {
		return featureRecall.get(f);
	}

	private double getFeatureCHI(String f) {

		int colon = f.lastIndexOf(":");
		String p = f.substring(0, colon);
		String c = f.substring(colon + 1, f.length());
		double n = itemCount;
		double n11 = U.tryGet(featureCount, f);
		double n10 = predicateCount.get(p) - n11;
		double n01 = labelCount.get(c) - n11;
		double n00 = itemCount - n11 - n10 - n01;
		double n1_ = predicateCount.get(p);
		double n_1 = labelCount.get(c);
		double n0_ = itemCount - n1_;
		double n_0 = itemCount - n_1;
		if (n00 < 0 || n01 < 0 || n10 < 0 || n11 < 0 || n_0 < 0 || n0_ < 0
				|| n1_ < 0 || n_1 < 0) {
			System.err.println("featureStore fail!");
			System.exit(-1);
		}
		double e11 = n1_ * n_1 / n;
		double e10 = n1_ * n_0 / n;
		double e01 = n0_ * n_1 / n;
		double e00 = n0_ * n_0 / n;

		double res = Math.pow(n11 - e11, 2) / e11 + Math.pow(n10 - e10, 2)
				/ e10 + Math.pow(n01 - e01, 2) / e01 + Math.pow(n00 - e00, 2)
				/ e00;

		String pp = "sourc";
		if (f.equals(pp + ":course")) {
			System.out.println();
			for (String l : labelCount.keySet())
				System.out.println(l + ": " + featureCount.get(pp + ":" + l));
			// System.out.println(l+": "+labelCount.get(l));
		}

		return res;
	}

	private double getPredicateCHI(String p) {
		double res = 0;
		for (String label : labelCount.keySet()) {
			res += ((double) predicateCount.get(p)) / itemCount
					* U.tryGetD(featureCHI, p + ":" + label);
		}
		return res;
	}

	double getMutualInfo(String f) {
		int colon = f.lastIndexOf(":");
		String p = f.substring(0, colon);
		String c = f.substring(colon + 1, f.length());
		double n = itemCount;
		double n11 = featureCount.get(f);
		double n10 = predicateCount.get(p) - n11;
		double n01 = labelCount.get(c) - n11;
		double n00 = itemCount + n11 - n10 - n01;
		double n1_ = predicateCount.get(p);
		double n_1 = labelCount.get(c);
		double n0_ = itemCount - n1_;
		double n_0 = itemCount - n_1;
		if (n00 < 0 || n01 < 0 || n10 < 0 || n11 <= 0 || n_0 < 0 || n0_ < 0
				|| n1_ < 0 || n_1 < 0) {
			System.err.println("featureStore fail!");
			System.exit(-1);
		}
		// double res= n11/n* log(n*n11/n1_/n_1)
		// +n01/n* log(n*n01/n0_/n_1)
		// +n10/n* log(n*n10/n1_/n_0)
		// +n00/n* log(n*n00/n0_/n_0);

		double res = n1_ / n * n11 / n1_ * log(n11 / n1_) + n1_ / n * n10 / n1_
				* log(n10 / n1_) + n0_ / n * n01 / n0_ * log(n01 / n0_) + n0_
				/ n * n00 / n0_ * log(n00 / n0_);
		return res;
	}

	public double getMeasure(String f) {
		return featureCHI.get(f);
	}

	private double getAdjustedPrecision(String f) {
		double p = featurePrecision.get(f);
		double zero = 1.0 / labelCount.size();
		if (p > zero)
			return U.linerTransform(zero, 1, 0, 1, p);
		else
			return U.decreaseTransform(U.linerTransform(zero, 0, 0, 1, p));
	}

	public double getNormalizedPrecision(String f) {
		double sum = 0;
		for (String l : labelCount.keySet())
			sum += U.tryGetD(featureRecall, f.substring(0, f.lastIndexOf(":"))
					+ ":" + l);
		return featureRecall.get(f) / sum;
	}

	double log(double d) {
		if (d == 0)
			return -Double.MAX_VALUE;
		return Math.log(d);
	}

	public void checkConsistency(HashSet<String> featureSet) {

		HashMap<String, ArrayList<KeyValuePair>> hm = new HashMap<String, ArrayList<KeyValuePair>>();
		for (String l : labelCount.keySet())
			hm.put(l, new ArrayList<KeyValuePair>());

		for (String feature : featureSet) {
			String l = U.getFL(feature);
			hm.get(l).add(new KeyValuePair(feature, getMeasure(feature)));
		}

		int n=0;
		for (String l : labelCount.keySet()) {
			ArrayList<KeyValuePair> al = hm.get(l);
			U.sort(al);
			for (int i = 0; i < al.size() - 1; i++) {
				String f1 = al.get(i).key;
				String f2 = al.get(i + 1).key;
				double p1 = precision(f1);
				double p2 = precision(f2);
				double zero = 1.0 / labelCount.size();
				if (p1 > zero && p2 > zero) {
					double r1 = featureCount.get(f1);
					double r2 = featureCount.get(f2);
					if (p2 >= p1 && r2 > r1) {
						System.out.println(r1+"    "+p1);
						//System.exit(-1);
						n++;
					}
				}
			}
		}
		System.out.println(n+"   _____");

	}

}
